#include "chatservice.hpp" 
#include "json.hpp"
#include "public.hpp"
#include <muduo/base/Logging.h>
#include <vector>

using namespace muduo;

ChatService::ChatService()
{
    _msgHandlerMap.insert({LOGIN_MSG, std::bind(&ChatService::Login, this, _1, _2, _3)});
    _msgHandlerMap.insert({LOGINOUT_MSG, std::bind(&ChatService::Login, this, _1, _2, _3)});
    _msgHandlerMap.insert({REG_MSG, std::bind(&ChatService::Reg, this, _1, _2, _3)});

    _msgHandlerMap.insert({ONE_CHAT_MSG, std::bind(&ChatService::OneChat, this, _1, _2, _3)});
    _msgHandlerMap.insert({ADD_FRIEND_MSG, std::bind(&ChatService::AddFriend, this, _1, _2, _3)});

    _msgHandlerMap.insert({CREATE_GROUP_MSG, std::bind(&ChatService::CreateGroup, this, _1, _2, _3)}); 
    _msgHandlerMap.insert({ADD_GROUP_MSG, std::bind(&ChatService::AddGroup, this, _1, _2, _3)}); 
    _msgHandlerMap.insert({GROUP_CHAT_MSG, std::bind(&ChatService::GroupChat, this, _1, _2, _3)}); 

    if (_redis.Connect()) {
        _redis.InitNotifyHandler(std::bind(&ChatService::HandleRedisSubscribeMessage, this, _1, _2));
    }
}

ChatService* ChatService::GetInstance()
{
    static ChatService service;
    return &service;
}

MsgHandler ChatService::GetHandler(int msgid)
{
    auto it = _msgHandlerMap.find(msgid);
    if (it == _msgHandlerMap.end()) {
        return [=](const TcpConnectionPtr& pt, json &js, Timestamp time) {
            LOG_ERROR << "msgid: " << msgid << " can not find handler!";
        };
    }
    else {
        return _msgHandlerMap[msgid]; 
    }
}

void ChatService::ClientCloseException(const TcpConnectionPtr& pt)
{
    User user;
    {
        std::lock_guard<std::mutex> lock(_connMapMutex);
        for (auto it = _userConnMap.begin(); it != _userConnMap.end(); ++it) {
            if (it->second == pt) {
                user.SetId(it->first);
                _userConnMap.erase(it);
                break;
            }
        }
    }
    
    // 向redis取消订阅
    _redis.Unsubscribe(user.GetId());

    if (user.GetId() != -1) {     
        user.SetState("offline");
        _userModel.UpdateState(user);
    }
}

void ChatService::Reset()
{
    // 把online状态的用户设置成offline
    _userModel.ResetState();
}

void ChatService::Login(const TcpConnectionPtr& pt, json& js, Timestamp time)
{
    int id = js["id"].get<int>();
    std::string pwd = js["password"];

    User user = _userModel.Query(id);
    if (user.GetId() == id && user.GetPassword() == pwd) {
        if (user.GetState() == "online") {
            // 已登录,不允许重复登录
            json response;
            response["msgid"] = LOGIN_MSG_ACK;
            response["errno"] = 2;
            response["errmsg"] = "this account is using, input another!";
            pt->send(response.dump());
        }
        else {
            // 登录成功,记录用户连接信息
            {
                std::lock_guard<std::mutex> lock(_connMapMutex);
                _userConnMap.insert({id, pt});
            }

            // 向redis订阅通道
            _redis.Subscribe(id);

            // 更新用户状态信息
            user.SetState("online");
            _userModel.UpdateState(user);

            json response;
            response["msgid"] = LOGIN_MSG_ACK;
            response["errno"] = 0;
            response["id"] = user.GetId();
            response["name"] = user.GetName();
            // 查询该用户是否有离线消息
            std::vector<std::string> offlineMsg = _offlineMsgModel.Query(id);
            if (!offlineMsg.empty()) {
                response["offlinemsg"] = offlineMsg;
                // 取出离线消息后,移除db中的离线消息
                _offlineMsgModel.Remove(id);
            }

            // 查询该用户的好友信息
            std::vector<User> userVec = _friendModel.Query(id);
            if (!userVec.empty()) {
                std::vector<std::string> userVec2;
                for (User& user : userVec) {
                    json js;
                    js["id"] = user.GetId();
                    js["name"] = user.GetName();
                    js["state"] = user.GetState();
                    userVec2.push_back(js.dump());
                }
                response["friends"] = userVec2;
            }

            // 查询该用户的群组信息
            std::vector<Group> groupVec = _groupModel.QueryGroups(id);
            if (!groupVec.empty()) {
                std::vector<std::string> groupVec2;
                for (Group& group : groupVec) {
                    json grpjson;
                    grpjson["id"] = group.GetId();
                    grpjson["groupname"] = group.GetName();
                    grpjson["groupdesc"] = group.GetDesc();
                    std::vector<string> userVec;
                    for (GroupUser& user : group.GetUsers()) {
                        json js;
                        js["id"] = user.GetId();
                        js["name"] = user.GetName();
                        js["state"] = user.GetState();
                        js["role"] = user.GetRole();
                        userVec.push_back(js.dump());
                    }
                    grpjson["users"] = userVec;
                    groupVec2.push_back(grpjson.dump());
                }
                response["groups"] = groupVec2;
            }
            pt->send(response.dump());
        }
    }
    else {
        // 该用户不存在
        json response;
        response["msgid"] = LOGIN_MSG_ACK;
        response["errno"] = 1;
        response["errmsg"] = "用户名或密码错误!";
        pt->send(response.dump());
    }
}

void ChatService::Reg(const TcpConnectionPtr& pt, json& js, Timestamp time)
{
    std::string name = js["name"];
    std::string pwd = js["password"];

    User user;
    user.SetName(name);
    user.SetPassword(pwd);

    bool result = _userModel.Insert(user);
    if (result) {
        // 注册成功
        json response;
        response["msgid"] = REG_MSG_ACK;
        response["errno"] = 0;
        response["id"] = user.GetId();
        pt->send(response.dump());
    }
    else {
        // 注册失败
        json response;
        response["msgid"] = REG_MSG_ACK;
        response["errno"] = 1;
        pt->send(response.dump());
    }
}

void ChatService::LoginOut(const TcpConnectionPtr& pt, json& js, Timestamp time)
{
    int userid = js["id"].get<int>();
    {
        std::lock_guard<std::mutex> lock(_connMapMutex);
        auto it = _userConnMap.find(userid);
        if (it != _userConnMap.end()) {
            _userConnMap.erase(it);
        }
    }

    // 向redis取消订阅
    _redis.Unsubscribe(userid);

    // 更新用户状态
    User user(userid, "", "", "offline");
    _userModel.UpdateState(user);
}

void ChatService::OneChat(const TcpConnectionPtr& pt, json& js, Timestamp time)
{
    int toid = js["toid"].get<int>();
    {
        std::lock_guard<std::mutex> lock(_connMapMutex);
        auto it = _userConnMap.find(toid);
        if (it != _userConnMap.end()) {
            // toid在线,转发消息
            it->second->send(js.dump());
            return;
        }
    }

    // 查询toid是否在redis订阅频道中
    User user = _userModel.Query(toid);
    if (user.GetState() == "online") {
        _redis.Publish(toid, js.dump());
        return;
    }

    // toid不在线,存储离线消息
    _offlineMsgModel.Insert(toid, js.dump());
}

void ChatService::AddFriend(const TcpConnectionPtr& pt, json& js, Timestamp time)
{
    int userid = js["id"].get<int>();
    int friendid = js["friend"].get<int>();

    // 存储好友信息
    _friendModel.Insert(userid, friendid);
}

void ChatService::CreateGroup(const TcpConnectionPtr& pt, json& js, Timestamp time)
{
    int userid = js["id"].get<int>();
    std::string name = js["groupname"];
    std::string desc = js["groupdesc"];

    // 存储新创建的群组信息    
    Group group(-1, name, desc);
    if (_groupModel.CreateGroup(group)) {
        // 存储群组创建人的信息
        _groupModel.AddGroup(userid, group.GetId(), "creator");
    }
}

void ChatService::AddGroup(const TcpConnectionPtr& pt, json& js, Timestamp time)
{
    int userid = js["id"].get<int>();
    int groupid = js["groupid"].get<int>();
    _groupModel.AddGroup(userid, groupid, "normal");
}

void ChatService::GroupChat(const TcpConnectionPtr& pt, json& js, Timestamp time)
{
    int userid = js["id"].get<int>();
    int groupid = js["groupid"].get<int>();
    std::vector<int> useridVec = _groupModel.QueryGroupUsers(userid, groupid);

    {
        std::lock_guard<std::mutex> lock(_connMapMutex);
        for (int id : useridVec) {
            auto it = _userConnMap.find(id);
            if (it != _userConnMap.end()) {
                // 转发消息
                it->second->send(js.dump());
            }
            else {
                // 查询toid是否在线
                User user = _userModel.Query(id);
                if (user.GetState() == "online") {
                    _redis.Publish(id, js.dump());
                }
                else {
                    // 存储离线消息
                    _offlineMsgModel.Insert(id, js.dump());
                }
            }
        }
    }
}

void ChatService::HandleRedisSubscribeMessage(int userid, std::string msg)
{
    std::lock_guard<std::mutex> lock(_connMapMutex);
    auto it = _userConnMap.find(userid);
    if (it != _userConnMap.end()) {
        it->second->send(msg);
        return;
    }

    // 存储该用户的离线消息
    _offlineMsgModel.Insert(userid, msg);
}